S3のアクセスコントロールが多すぎて訳が解らないので整理してみる
Amazon S3のアクセスコントロールがわけわからない
いくつもあってわけがわかりませんね。それぞれ何が出来て何ができないのか、どうゆう時にどれを使うのか、組み合わせて使うとどうなるのか、いい機会なので整理してみたいと思います。
ACL
これはおそらくいちばん古くからある機能です。
ManagementConsoleではPermissionsというところで設定ができます。
ACLの設定はバケット、もしくはオブジェクト単位で設定する事が出来るのが特徴になっています。
APIを見てみても、putBucketACLとputObjectACLとあることから、Bucket単位でのACLとObject単位でのACLがあることが分かります。
別のアカウントにまたがって設定することも出来ます。
BucketPolicy
S3のBucketに対して、PolicyDocumentを使ってアクセスコントロールを設定出来ます。
{ "Id": "bucketpolicy-${oacl}-${acl}-${EFFECT}", "Statement": [ { "Sid": "Stmt${UNIQ}-1", "Action": [ "s3:GetObject" ], "Effect": "${EFFECT}", "Resource": "arn:aws:s3:::${BUCKET}/*", "Principal": { "AWS": [ "${TARGET_PRINCIPAL}" ] } }, { "Sid": "Stmt${UNIQ}-2", "Action": [ "s3:ListBucket" ], "Effect": "${EFFECT}", "Resource": "arn:aws:s3:::${BUCKET}", "Principal": { "AWS": [ "${TARGET_PRINCIPAL}" ] } } ] }
こんな感じで、Action, Effect, Principal, Resourceと、これらを組み合わせて柔軟なアクセスコントロールが実現出来ます。
この一個のファイルだけを公開したいとかそういった用途に使うのには向いていないように思います。ObjectACLを使いましょう。
個人的な感覚ですが、bucketACLはBucketPolicyに置き換えたほうが良いなと思っています。
ACLとBucketPolicyどちらが強いのか
気になってしまったので、ACLとBucketPolicyどちらが強いのかを検証したいと思います。
検証の方法は簡単です。
ACLは許可するか、何もしないかの2値です。BucketPolicyは許可する、拒絶する、何もしないの3値です。
ACLはBucketとObjectがありますので、この3つの設定を全通りの組み合わせでやってみます。
BucketACL | ObjcetACL | BucketPolicy | 結果 |
---|---|---|---|
許可 | 許可 | 拒否 | 失敗 |
許可 | 未設定 | 拒否 | 失敗 |
許可 | 許可 | 許可 | 成功 |
許可 | 未設定 | 許可 | 成功 |
許可 | 許可 | 未設定 | 成功 |
許可 | 未設定 | 未設定 | 失敗 |
未設定 | 許可 | 拒否 | 失敗 |
未設定 | 未設定 | 拒否 | 失敗 |
未設定 | 許可 | 許可 | 成功 |
未設定 | 未設定 | 許可 | 成功 |
未設定 | 許可 | 未設定 | 成功 |
未設定 | 未設定 | 未設定 | 失敗 |
この結果からもわかるように、[Bucket|Object]ACLよりもBucketPolicyの方が強くなります。
BucketPolicyで設定されていない場合は、ObjcetACL , BucketACLによって制御されています。
これについては、公式にドキュメントでここやここにも書いて有ります。
さらにBucketPolicyではIPアドレスで制御やRefererで制御も出来ます。
直リン禁止とか出来たりします。
IAM(IAMRole)
IAMではPolicyDocumentを使用してアクセスをコントロールします。
IAMとBucketPolicyの違いはユーザ(クライアント)側に紐付いて権限が与えられるということです。
また、現在はPolicyに変数を利用する事が出来るため、ユーザ名に応じたアクセスのコントロールが可能になっています。
例えば、こんな感じです。
policyDocumentに
{ "Version": "2012-10-17", "Statement": [ { "Action": [ "s3:GetObject", "s3:PutObject" ], "Effect": "Allow", "Resource": ["arn:aws:s3:::BUCKET/${aws:username}/*"] } ] }
と記述されていたら、
IAMuser名: akeri の時には s3://BUCKET/akeri/ で始まるオブジェクトにアクセス出来る事になります。
BucketPolicy同様にconditionによって、条件を指定する事も出来ます。
IPアドレスで制御したり、
@see AWS Identity and Access Management
で、IAMとBucketPolicyはどっちが強いの?
先ほどACLとBucketPolicyを試してみたら、BucketPolicyが強かったのですが、今度はIAMとBucketPolicyどっちが強いのかを試してみたいと思います。
さっきと同様に総当りで調べてみます。
今回ACLは何も設定していない状態です。
BucketPolicy | IAM | 結果 |
---|---|---|
拒否 | 拒否 | 失敗 |
拒否 | 許可 | 失敗 |
許可 | 拒否 | 失敗 |
許可 | 許可 | 成功 |
未設定 | 拒否 | 失敗 |
未設定 | 許可 | 成功 |
結果はこんな感じです。
とりあえず、IAMで許可されていないと確実に失敗します。
当たり前といえば当たり前な気がします。
しかし、IAMで許可されていたとしてもBucketPolicyで拒否されているとアクセス出来ませんでした。
とりあえず、ここから わかることは拒否は絶対ということです。
IAMで拒否されていてもBucketPolicyで拒否されていても、失敗します。
成功するのは、IAMで許可されているかつBucketPolicyで拒否されていない(Allowもしくは未設定)の場合でした。
さっきのACLとBucketPolicyではわかりませんでしたが、BucketPolicyが優先されているわけではなくて、Denyが優先されているようです。
まとめ
上記の結果から、S3のACLに関するルールがなんとなく見えてきました。
ACLの基本として、どこかしらでDenyとなっていたら、ほかでAllowだろうとDenyになってしまいます。
Denyに対して、Allowで上書きは出来ません。
それとは逆にBucketPolicyやACLでAllowを設定していても、IAMでDenyであった場合は当然アクセス出来ません。
ここから考えるベストプラクティスというのはパットは出てきませんが、ACLはあるオブジェクト一つだけを公開したい時や、publicにする時に使う以外では使わないのかもしれません。
まとめるとタイトルで言ったものの、あまりまとめられてる感じがしないですね。ごめんなさい。
バケットとして不変的なアクセスコントロールはBucketPolicyで定義し、IAMはユーザ毎に変更になるようなものを設定するのが良いのではと思います。この時にBucketPolicyでDenyを付けてしまうと、IAMでいくら設定してもDenyなので、今の段階で私の考えられるベターな方法は、まずは必要なところだけBucketPolicyで公開します。その公開している範囲ないで更に限定的に拒否したいところにはDenyで上書いてあげる方法が良さそうです。
さらにIAMでは変数が使えるので、グループのPolicyで一括設定もできるので、複数のユーザがいて、あるルールに基づいてアクセスをコントロールしたい場合にはIAMを使いたいですね。
用途をまとめるとこんな感じかなと思います。◯はいいじゃんで、☓は出来ない。△はやってやれないことは無いけど、めんどくさすぎてやりたくない位なゆるい判断基準で付けてます。
ユースケース | [Bucket|Object]ACL | BucketPolicy | IAM |
---|---|---|---|
一個のObjectを公開したい | ◯ | △ | △ |
バケット全体を公開したい | △ | ◯ | ◯ |
特定のユーザにだけ公開したい | △ | △ | ◯ |
IPアドレス制限をかけたい | ☓ | ◯ | ◯ |
Bucketの中のPrefixを指定して公開したい | △ | ◯ | ◯ |
prefixがユーザ毎に可変 | △ | △ | ◯ |
おまけ
今回調査に使ったシェルスクリプト載せておきます。
権限毎にバケット何個も作って、オブジェクトも作って、アクセスしてみて、終わったら削除してという感じのものです。
#!/bin/bash UNIQ=$(date +'%Y%m%d%I%M%S') alias aws='aws --region ap-northeast-1' BUCKET_PREFIX=akeri-acl-test-$UNIQ- TARGET_PRINCIPAL="arn:aws:iam::123456789012:user/s3acltest" TARGET_MAIL="[email protected]" # create Bucket Policy Document UPLOADFILE=akeridayo.txt IAM_USERNAME=s3acltest echo "akeridayo" > $UPLOADFILE echo "created upload file" echo "<table>" > result.html # bucket create # 0 deny # 1 allow # 2 none for acl in `seq 2 2` do for policy in `seq 0 2` do BUCKET=${BUCKET_PREFIX}${acl}${policy} # create bucket aws s3api create-bucket --bucket $BUCKET if [ $acl -eq 1 ]; then aws s3api put-bucket-acl --bucket $BUCKET --grant-read emailaddress="$TARGET_MAIL" echo "putted bucket acl" fi echo "created bucket $BUCKET" # set Bucket Policy if [ $policy -ne 2 ]; then EFFECT="Allow" if [ $policy -eq 0 ]; then EFFECT="Deny" fi #========================================= policy.json cat << _EOT_ > bucketPolicy-${UNIQ}-${acl}-${EFFECT}.json { "Id": "bucketpolicy-${oacl}-${acl}-${EFFECT}", "Statement": [ { "Sid": "Stmt${UNIQ}-1", "Action": ["s3:GetObject"], "Effect": "${EFFECT}", "Resource": "arn:aws:s3:::${BUCKET}/*", "Principal": {"AWS": ["${TARGET_PRINCIPAL}" ]}}, { "Sid": "Stmt${UNIQ}-2", "Action": ["s3:ListBucket"], "Effect": "${EFFECT}", "Resource": "arn:aws:s3:::${BUCKET}", "Principal": {"AWS": ["${TARGET_PRINCIPAL}"]} } ] } _EOT_ #========================================================== # set Bucket ACL Allow aws s3api put-bucket-policy --bucket $BUCKET --policy file://$(pwd)/bucketPolicy-${UNIQ}-${acl}-${EFFECT}.json echo "putted bucket policy" rm bucketPolicy-${UNIQ}-${acl}-${EFFECT}.json fi # set Bucket Policy END #___Object ACL Loop Start for oacl in `seq 2 2` do key=${acl}${oacl} aws s3api put-object --bucket $BUCKET --key $key --body $UPLOADFILE echo "file uploaded " if [ $oacl -eq 1 ]; then aws s3api put-object-acl --bucket $BUCKET --key $key --grant-read emailaddress="$TARGET_MAIL",emailaddress="[email protected]" echo "putted object acl " fi aws s3api get-object --profile iamuser --bucket $BUCKET --key $key /dev/null if [ $? -eq 0 ]; then resultOther="成功" else resultOther="失敗" fi echo "getted Object other account $resultOther" for iam in `seq 0 1` do IAMEFFECT="Allow" if [ $iam -eq 0 ]; then IAMEFFECT="Deny" fi POLICYNAME=POLICY-${UNIQ}-${acl}-${oacl}-${policy}-${iam} POLICYFILE=file://$(pwd)/$POLICYNAME.json cat << _EOT_ > $POLICYNAME.json {"Statement": [ { "Sid": "Stmt${UNIQ}2", "Action": ["s3:GetObject"], "Effect": "${IAMEFFECT}", "Resource": "arn:aws:s3:::${BUCKET}/*" }]} _EOT_ echo "created policy document" aws iam put-user-policy --policy-name $POLICYNAME --user-name $IAM_USERNAME --policy-document $POLICYFILE if [ $? -eq 0 ]; then echo "put user policy OK" fi echo "putted user policy" rm $POLICYNAME.json sleep 5 aws s3api get-object --profile iamuser --bucket $BUCKET --key $key out if [ $? -eq 0 ]; then resultIAM="成功" else resultIAM="失敗" fi echo "get object IAM User $resultIAM" aws iam delete-user-policy --policy-name $POLICYNAME --user-name $IAM_USERNAME #output html cat << _EOT_ >> result.html <tr> <td>${acl}</td> <td>${oacl}</td> <td>${policy}</td> <td>${resultOther}</td> <td>${iam}</td> <td>${resultIAM}</td> </tr> _EOT_ done aws s3api delete-object --bucket $BUCKET --key $key done aws s3api delete-bucket --bucket $BUCKET done done echo "</table>" >> result.html cat result.html |perl -pe "s/>1</>許可</g" |perl -pe "s/>2</>未設定</g" |perl -pe "s/>0</>拒否</g" > index.html rm result.html